/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package pkg4ax.mc;

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.TimelineBuilder;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Service;
import javafx.concurrent.Worker;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.Accordion;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeView;
import javafx.scene.effect.Effect;
import javafx.scene.effect.Glow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.util.Callback;
import javafx.util.Duration;
import javax.imageio.ImageIO;
import org.apache.commons.io.FilenameUtils;
import org.json.simple.JSONObject;
import pkg4ax.mc.graphics.AwardList;
import pkg4ax.mc.graphics.BreadcrumbBar;
import pkg4ax.mc.graphics.CustomFileChooser;
import pkg4ax.mc.graphics.CustomFileChooser2;
import static pkg4ax.mc.graphics.FilePathTreeItem.fei;
import pkg4ax.mc.graphics.GenreList;
import pkg4ax.mc.graphics.MovieTreeItem;
import pkg4ax.mc.graphics.Top5CustomMenuItem;
import pkg4ax.mc.graphics.TreeViewSearchBox;
import pkg4ax.mc.graphics.WindowButtons;
import pkg4ax.mc.listeners.ModalDimmerEventHandler;
import pkg4ax.mc.listeners.MyListChangeListener;
import pkg4ax.mc.listeners.NumberListener;
import pkg4ax.mc.listeners.SearchHandler;
import pkg4ax.mc.listeners.TreeCellEventHandler;
import pkg4ax.mc.listeners.TreeCellEventHandler2;
import pkg4ax.mc.localServices.AddMovieService;
import pkg4ax.mc.localServices.AlreadyWatchedService;
import pkg4ax.mc.localServices.DeleteMovieService;
import pkg4ax.mc.localServices.GetMovieInformationsService;
import pkg4ax.mc.localServices.LoadMessagesService;
import pkg4ax.mc.localServices.OpenCollectionService;
import pkg4ax.mc.localServices.PlayMovieService;
import pkg4ax.mc.localServices.RefreshMovieInformationService;
import pkg4ax.mc.localServices.SaveCollectionService;
import pkg4ax.mc.localServices.SaveMessagesService;
import pkg4ax.mc.localServices.SearchTitleService;
import pkg4ax.mc.localServices.SetMovieCorrectService;
import pkg4ax.mc.serverServices.DeleteContactService;
import pkg4ax.mc.serverServices.GetProfileInfoService;
import pkg4ax.mc.serverServices.LogoutService;
import pkg4ax.mc.serverServices.SaveProfileChangesService;
import pkg4ax.mc.serverServices.SendRequestService;
import pkg4ax.mc.serverServices.SendSuggestionService;
import pkg4ax.mc.serverServices.GetGenresService;
import pkg4ax.mc.serverServices.GetAwardsService; 
import pkg4ax.mc.tasks.Indexer;
import pkg4ax.mc.tasks.Searcher;
import pkg4ax.mc.tasks.ThreadClient;
import pkg4ax.mc.utilities.Contact;
import pkg4ax.mc.utilities.EffectUtilities;
import pkg4ax.mc.utilities.IndexMovieTreeItem;
import pkg4ax.mc.utilities.Movie;
import pkg4ax.mc.utilities.Request;
import pkg4ax.mc.utilities.Suggestion;

/**
 * MainWindowController.class
 * @author 4is
 */
public class MainWindowController implements Initializable {
    
    private static MainWindowController mainWindowController;
    public CustomFileChooser fileChooser;
    public CustomFileChooser2 openCollectionFileChooser;
    public BreadcrumbBar breadcrumbBar;
    public VBox searchBoxBar;
    public TreeViewSearchBox searchBox;
    public MovieTreeItem allRootNode;
    public MovieTreeItem resultsRootNode;
    private Indexer indexer;
    public Searcher searcher;
    @FXML public VBox dragNode;
    @FXML public StackPane modalDimmer;
    @FXML public ToggleButton buttonAll;
    @FXML public ToggleButton buttonResults;
    @FXML public Label pressedButtonInfo;
    @FXML public HBox movieCollectionToolBar;
    @FXML public HBox rightBar;
    @FXML public HBox treeViewBox;
    @FXML public TreeView treeView;
    @FXML public StackPane treeStack;
    @FXML public Region stackRegion;
    @FXML public ProgressIndicator stackProgressIndicator;
    @FXML public Button openCollectionButton;
    @FXML public Button addMoviesButton;
    @FXML public Button deleteMoviesButton;
    @FXML public Button saveCollectionButton;
    @FXML public Button refreshMoviesButton;
    @FXML public Button playMovieButton;
    @FXML public Button alreadyWatchedButton;
    @FXML public Button checkmarkButton;
    @FXML public Label progressIndicatorLabel;
    @FXML public HBox collectionNewNameHBox;
    @FXML public TextField collectionNewNameTextField;
    @FXML public Button collectionNewNameContinueButton;
    @FXML public Button collectionNewNameCancelButton;
    @FXML public CheckBox collectionNewNameCheckBox;
    @FXML public VBox addMovieDialog;
    @FXML public VBox internetFailure;
    @FXML public Button internetFailureOkButton;
    @FXML public VBox confirmDelete;
    @FXML public Button confirmDeleteYesButton;
    @FXML public Button confirmDeleteNoButton;
    @FXML public VBox optionsMenu;
    @FXML public TextField optionsMenuNameField;
    @FXML public TextField optionsMenuTitleField;
    @FXML public TextField optionsMenuYearField;
    @FXML public Button optionsMenuOkButton;
    @FXML public Button optionsMenuRefreshButton;
    @FXML public Button optionsMenuCloseButton;
    @FXML public Accordion suggestions;
    @FXML public Accordion requests;
    @FXML public Button messagesButton;
    @FXML public VBox requestsMessagesWindow;
    @FXML public Button logoutButton;
    @FXML public Label unreadMessagesAnnotation;
    @FXML public Button nextSelectedMovieButton;
    @FXML public Button suggestionOkButton;
    @FXML public TextField movieLinkTextField;
    @FXML public TextField movieSubsTextField;
    @FXML public Label movieTitleLabel;
    @FXML public VBox suggestMovieDialog;
    @FXML public Button profileButton;
    @FXML public ListView<Integer> contactList;
    @FXML private Button addContact;
    @FXML private Button deleteContact;
    @FXML private Button suggestToContact;
    @FXML public TextField nameField;
    @FXML public StackPane adder;
    @FXML public Label addLabel;
    @FXML public StackPane checker;
    @FXML public VBox checkerVB;
    @FXML public Label topCheckerLabel;
    @FXML public Button checkerButtonYes;
    @FXML public Button checkerButtonNo;
    @FXML public VBox myProfileSplitPane;      //*********myProfile SplitPane
    @FXML public AnchorPane mainAnchorPane;
    @FXML public AnchorPane photoAnchorPane;
    @FXML public AnchorPane infoAnchorPane;
    @FXML public AnchorPane bottomRightAnchorPane;
    @FXML public AnchorPane nameLabelAnchorPane;
    @FXML public AnchorPane bottomLeftAnchorPane;
    @FXML public Button changePictureButton;
    @FXML public Button editInfoButton;
    @FXML public Label userNameLabel;
    @FXML public Label nickNameLabel;
    @FXML public Label emailLabel;
    @FXML public Label thankedByLabel;
    @FXML public Label countryLabel;
    @FXML public Label cityLabel;
    @FXML public Label birthdayLabel;
    @FXML public TextField nickNameTextF;
    @FXML public TextField emailTextF;
    @FXML public TextField thankedByTextF;
    @FXML public TextField countryTextF;
    @FXML public TextField cityTextF;
    @FXML public TextField birthdayTextF;
    @FXML public VBox bottomLeftVBox;;
    @FXML public VBox stableVBox;
    @FXML public VBox mainInfoVBox;
    @FXML public ImageView imgView;
    @FXML public VBox photoVBox;
    @FXML public Button saveChanges;          //***********myProfile SplitPane    
    @FXML public TextField top5;
    @FXML public Button closeProfileButton;
    @FXML public HBox HboxTop1;
    @FXML public HBox HboxTop2;
    @FXML public HBox HboxTop3;
    @FXML public HBox HboxTop4;
    @FXML public HBox HboxTop5;
    
            
//==================================================================MOVIE PANE FXML IDS ===========================//
    
    @FXML public Label movieName;
    @FXML public Label movieYear;
    @FXML public Label language;
    @FXML public Label rated;
    @FXML public Label duration;
    @FXML public Label releaseDate;
    @FXML public TextArea genres;
    @FXML public Label ratingNumber;
    @FXML public Label votes;
    @FXML public Label movieType;
    @FXML public Label metascore;
    @FXML public Label likes;
    @FXML public Label dislikes;
    @FXML public Label suggestedTimes;
    @FXML public TextArea starList;
    @FXML public ImageView stars;
    @FXML public Label starNumber;
    @FXML public TextArea writer;
    @FXML public TextArea director;
    @FXML public Label fileDuration;
    @FXML public Label bitrate;
    @FXML public Label watched;
    @FXML public Label videoFormat;
    @FXML public Label audioFormat;
    @FXML public Label creationTime;
    @FXML public Button like;
    @FXML public Button dislike;
    @FXML public TextArea plotArea;
    @FXML public ListView awardMoviePaneList;
    @FXML public ImageView poster;
    @FXML public HBox starRating;
    @FXML public TextArea countryMovie;
    @FXML public Label lastView;
    
//===================================================SEARCH FXML IDS =========================================//
    @FXML public Pane searchPane;
    @FXML public ImageView logo;
    @FXML public TextField searchField;
    @FXML public HBox mainToolBar;
    @FXML public ToggleButton advancedBtn;
    @FXML public Button searchBtn;
    @FXML public Group searchGroup;
    @FXML public Label warningLabel;
    @FXML public TextField fromYear;
    @FXML public TextField toYear;
    @FXML public TextField fromLikes;
    @FXML public TextField toLikes;
    @FXML public TextField fromRating;
    @FXML public TextField toRating;
    @FXML public TextField fromVotes;
    @FXML public TextField toVotes;
    @FXML public TextField fromSuggestions;
    @FXML public TextField toSuggestions;
    @FXML public TextField country;
    @FXML public TextArea plot;
    @FXML public CheckBox isActor;
    @FXML public CheckBox isDirector;
    @FXML public CheckBox isWriter;
    @FXML public CheckBox isSeries;
    @FXML public VBox awardVBox;
    @FXML public VBox genreVBox;
    @FXML public StackPane advancedPane;
    @FXML public Accordion advancedAccordion;
    @FXML public HBox windowVBox;
          public WindowButtons windowBtns;
          public GenreList genreList;
          public AwardList awardList;
          
    // Services
    public AddMovieService service = new AddMovieService();
    public OpenCollectionService oservice = new OpenCollectionService();
    public DeleteMovieService dservice = new DeleteMovieService();
    public SaveCollectionService sservice = new SaveCollectionService();
    public AlreadyWatchedService aservice = new AlreadyWatchedService();
    protected PlayMovieService pservice = new PlayMovieService();
    public SetMovieCorrectService cservice = new SetMovieCorrectService();
    public RefreshMovieInformationService rservice = new RefreshMovieInformationService();
    public SearchTitleService tservice = new SearchTitleService();
    public SaveMessagesService mservice = new SaveMessagesService();
    protected LoadMessagesService lservice = new LoadMessagesService();
    protected GetMovieInformationsService gservice = new GetMovieInformationsService();
   
    // unread messages image
    protected static final Image message_read = new Image(MainWindowController.class.getResourceAsStream("graphics/images/read_message.png"));
    // no new messages, new-message image
    protected static final Image noMessages = new Image(MainWindowController.class.getResourceAsStream("graphics/images/no-messages.png"));
    protected static final Image newMessages = new Image(MainWindowController.class.getResourceAsStream("graphics/images/new-messages-red.png"));
    // contact offline
    public static Image offlineStatus = new Image(MainWindowController.class.getResourceAsStream("graphics/images/status-user-offline-icon.png"));
    // contact online
    public static Image onlineStatus = new Image(MainWindowController.class.getResourceAsStream("graphics/images/status-user-online-icon.png"));
    
    public static Image logoImg = new Image(MainWindowController.class.getResourceAsStream("graphics/images/logo.png"));
    // count unread messages, find next title to display in suggestion addLinks window, show nextSelectedMovie button
    public static IntegerProperty unreadMessages = new SimpleIntegerProperty(0);
    public static StringProperty nextTitle = new SimpleStringProperty();
    public static BooleanProperty next = new SimpleBooleanProperty();
    
    // no image icon
    public static Image initImage = new Image(MainWindowController.class.getResourceAsStream("graphics/images/no_image.png"));
    public static Image posterImg = new Image(MainWindowController.class.getResourceAsStream("graphics/images/no_image.png"));
    // user's id
    public static int userId;
    // user;s nickName
    public static String nickName;
    
    // displayed movies
    public static Map<Integer, Movie> displayedMovies = new HashMap<>(10);
    public static final Map<String, JSONObject> recentlyAddedMovies = new HashMap<>(10);
    // displayed contacts
    public static Map<Integer, Contact> displayedContacts = new HashMap<>(5);
    // application threads running
    public static int runningThreads = 0;
    
    // iterate through treeView selected items
    public static Iterator<MovieTreeItem> iterator;
    public static MovieTreeItem currentMovieTreeItem;
    
    // a list of suggestions to be sent
    public static List<pkg4ax.mc.utilities.Suggestion> movieSuggestionsToSend = new ArrayList<>(2);
    
    // current selected profile image
    public static File chosenProfilePhoto;
    
    @Override
    @SuppressWarnings("unchecked")
    public void initialize(URL url, ResourceBundle rb) {
        //static reference to MainWindowController for other classes to use
        mainWindowController = this;
        poster.setImage(posterImg);
        logo.setImage(logoImg);
        
        // glow effect for unreadMessagesAnnotation
        final Effect glow = new Glow(1.0);
        unreadMessagesAnnotation.setEffect(glow);
        // listen to unreadMessages counter
        final ChangeListener messagesListener = new ChangeListener<Integer>() {
            @Override
            public void changed(ObservableValue<? extends Integer> ov, Integer oldValue, Integer newValue) {
                if(newValue != null) {
                    if(newValue > 0)
                        messagesButton.setGraphic(new ImageView(newMessages));
                    else
                        messagesButton.setGraphic(new ImageView(noMessages));
                    
                    unreadMessagesAnnotation.setText(String.valueOf(newValue));
                }//if
            }//changed(ObservableValue<? extends integer>, Integer, Integer)
        };//ChangeListener<Integer>()
        unreadMessages.addListener(messagesListener);
        
        // load messages
        lservice.start();
        
        // set the main window stage dragable from the top bar
        EffectUtilities.makeDraggable(MC.applicationStage, dragNode);
        
        // create search box
        searchBox = new TreeViewSearchBox();
        // add it to parent VBox
        searchBoxBar.getChildren().add(searchBox);
        
        // create a ListChangeListener for allRootNode.getChildren()
        final MyListChangeListener childrenListener = new MyListChangeListener();
        
        // create a TreeCelleEventHandler for tree-cells
        final TreeCellEventHandler treeCellEventHandler = new TreeCellEventHandler();
        
        // create a treeCellEventhandler to set path to breadCrumbBar
        final TreeCellEventHandler2 treeCellEventHandler2 = new TreeCellEventHandler2();
        
        //initialize Results root node, by default expanded
        displayedMovies.put(-2, new Movie("Results"));
        resultsRootNode = new MovieTreeItem(new IndexMovieTreeItem(-2, 0, ""));
        resultsRootNode.setExpanded(true);
        
        // load most recent Collection displayed, initializes also allRootNode
        loadRecentCollection();
        
        // create a ModalDimmerEventHandler for modalDimmer
        final ModalDimmerEventHandler modalDimmerEventHandler = new ModalDimmerEventHandler();
        // handle modalDimmer mouse events, hide modalDimmer at startUp
        modalDimmer.setOnMouseClicked(modalDimmerEventHandler);
              
        // set style to stackRegion
        stackRegion.setStyle("-fx-background-color: rgba(0, 0, 0, 0.3)");
    
        // Group toggle Buttons All, Results
        ToggleGroup toggleGroup = new ToggleGroup();
        buttonAll.setToggleGroup(toggleGroup);
        buttonAll.setSelected(true);
        buttonResults.setToggleGroup(toggleGroup);
        toggleGroup.selectedToggleProperty().addListener(
            new ChangeListener<Toggle>() {
               @Override
               public void changed(ObservableValue<? extends Toggle> ov, Toggle oldToggle, Toggle newToggle) {
                   if(newToggle == buttonAll)
                       treeView.setRoot(allRootNode);
                   else if(newToggle == buttonResults)
                       treeView.setRoot(resultsRootNode);
                   //set to label pressedButtonInfo the number of the movies in current collection
                   //optionsMenu.setVisible(false);
                    pressedButtonInfo.setText((treeView.getRoot() != null)? 
                            treeView.getRoot().getChildren().size()  + " movies displayed..."
                            :
                            "0 movies displayed...");
               }//change(ObservableValue<? extends Toggle>, Toggle, Toggle)
            });//addListener(ChangeListener<Toggle>)    
        
        // intialize BreadcrumbBar;
        breadcrumbBar = new BreadcrumbBar();
        // add it to the rigth bar
        rightBar.getChildren().add(breadcrumbBar);
        
        // customize treeView behavior
        treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        
        // create a CustomMenuItem for the top5 assignment, add it to context menu, add contaxt menu to tree cell
        Top5CustomMenuItem top5CustomMenuItem = new Top5CustomMenuItem();
        final ContextMenu top5ContextMenu = new ContextMenu();
        top5CustomMenuItem.setHideOnClick(false);
        top5ContextMenu.getItems().add(top5CustomMenuItem);
        treeView.setContextMenu(top5ContextMenu);
        // customize tree-cell style and behavior
        treeView.setCellFactory(new Callback<TreeView<IndexMovieTreeItem>, TreeCell<IndexMovieTreeItem>>() {
            @Override
            public TreeCell<IndexMovieTreeItem> call(final TreeView<IndexMovieTreeItem> p) {
                return new TreeCell<IndexMovieTreeItem>() {
                    final ImageView iv = new ImageView();
                    @Override 
                    public void updateItem(final IndexMovieTreeItem imti, boolean empty) {
                        super.updateItem(imti, empty);
                        if(empty) {
                            setText(null);
                            setGraphic(null);
                            //remove tree-cell eventHandlers
                            removeEventHandler(MouseEvent.MOUSE_CLICKED, treeCellEventHandler);
                            removeEventHandler(MouseEvent.MOUSE_CLICKED, treeCellEventHandler2);
                        }else{
                            switch(imti.getPosition()) {
                                case 3:
                                    setText(imti.getText());
                                    Image fileImage = fei.get(FilenameUtils.getExtension(displayedMovies
                                                                            .get(imti
                                                                            .getIndex())
                                                                            .getFile()
                                                                            .toString())
                                                                            .toLowerCase());
                                    fileImage = (fileImage == null)? fei.get("_blank") : fileImage;
                                    iv.setImage(fileImage);
                                    setGraphic(iv);
                                    break;
                                case 2:
                                    setGraphic(null);
                                    setText(displayedMovies.get(imti.getIndex()).getMovieName());
                                    setId((displayedMovies.get(imti.getIndex()).getAlreadyWatched())?
                                        "treeCellGreen"
                                        :
                                        (displayedMovies.get(imti.getIndex()).getMovieAttributes().getMovie().isEmpty()
                                        || ((String)displayedMovies.get(imti.getIndex()).getMovieAttributes().getMovie().get("Response")).equalsIgnoreCase("false"))?
                                            "treeCellRed"
                                            :
                                            (displayedMovies.get(imti.getIndex()).getMovieAttributes().getScore() < 80)?
                                                "treeCellYellow"
                                                 :
                                                 "treeCellBlack");
                                    this.addEventHandler(MouseEvent.MOUSE_CLICKED, treeCellEventHandler);
                                    break;
                                default:
                                    setGraphic(null);
                                    setText(displayedMovies.get(imti.getIndex()).getMovieName());
                                    setId("treeCellBlack");
                            }//switch
                            //get current cell
                            imti.setCell(this);
                            this.addEventHandler(MouseEvent.MOUSE_CLICKED, treeCellEventHandler2);
                            setContextMenu(top5ContextMenu);
                        }//else
                    }//updateItem(IndexMovieTreeItem, boolean)
                };//TreeCell<IndexMovieTreeItem>
            }//call(TreeView<IndexMovieTreeItem>)
        });//setOnCellFactory(Callback<TreeView<IndexMovieTreeItem>, TreeCell<IndexMovieTreeItem>>)
        
        // initialize fileChooser for adding new movie(s)
        fileChooser = new CustomFileChooser(MC.applicationStage);
        // add Listener to addMoviesButton, show first addMovieDialog then fileChooser
        addMoviesButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(allRootNode == null || allRootNode.getValue().getIndex() == -3) {
                    collectionNewNameCheckBox.setSelected(true);
                    collectionNewNameCheckBox.setDisable(true);
                }//if
                addMovieDialog.setVisible(true);
                showModalMessage(addMovieDialog);
            }//handle(ActionEvent)            
        });//setOnAction(EventHandler<ActionEvent>)
        
        // customize the behavior of addMovieDialog
        collectionNewNameCancelButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                hideModalMessage();
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        addMovieDialog.setVisible(false);
                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                collectionNewNameCheckBox.setSelected(false);
                                collectionNewNameTextField.setText("");
                                collectionNewNameCheckBox.setDisable(false);
                            }//run()
                        });//runLater(Runnable)
                    }//run()
                });//runLater(Runnable)
            }//handle(ActionEvent)
        });//setOnAction(EventHandler<ActionEvent>)
        collectionNewNameContinueButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                //hideModalMessage();
                addMovieDialog.setVisible(false);
                showModalMessage(fileChooser);
            }//handle(ActionEvent)        
        });//setOnAction(EventHandler<ActionEvent>)
        collectionNewNameCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> ov, Boolean old_val, Boolean new_val) {
                collectionNewNameHBox.setDisable(!new_val);
                collectionNewNameContinueButton.setDisable((new_val) && (collectionNewNameTextField.getText().length() == 0));
            }//changed(ObservableValue<? extends Boolean>, Boolean, Boolean)
        });//addListener(ChangeListener<Boolean>)
        collectionNewNameTextField.textProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue<? extends String> ov, String t, String t1) {
                collectionNewNameContinueButton.setDisable((!collectionNewNameHBox.isDisabled()) 
                                                            && ((collectionNewNameTextField.getText().length() == 0)
                                                            || !collectionNewNameTextField.getText()
                .matches("(\\w+|\\s|\\~|\\`|\\@|\\#|\\$|\\%|\\^|\\&|\\(|\\)|\\_|\\-|\\+|\\=|\\}|\\{|\\[|\\]|\\;|\\,|\\.|\\!|\\')*")
                || new File("src/pkg4ax/mc/movie-collections/"+collectionNewNameTextField.getText()+".coll").exists()));
            }//changed(ObservableValue<? extends String>, String, String)
        });//addListener(ChangeListener<String>)
        
        // initialize fileChooser for opening collection files
        openCollectionFileChooser = new CustomFileChooser2(MC.applicationStage);
        // add Listener to openCollectionButton, show CustomFileChooser2
        openCollectionButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                showModalMessage(openCollectionFileChooser);
            }//handle(ActionEvent)            
        });//setOnAction(EventHandler<ActionEvent>)
        
        tservice.stateProperty().addListener(new ChangeListener<Worker.State>() {
            @Override
            public void changed(ObservableValue<? extends Worker.State> ov, Worker.State oldState, Worker.State newState) {
                if(newState == Worker.State.SUCCEEDED) {
                    bind(sservice);
                    SaveCollectionService.setCloseApp(false);
                    sservice.restart();
                    
                    //send movie informations to server
                    gservice.restart();
                }//if
            }//changed(ObservableValue<? extends Worker.State>, Worker.State, Worker.State)          
        });//addListener(ChangeListener<Worker.State>)
        // auto-save after AddMovieService is over
        service.stateProperty().addListener(new ChangeListener<Worker.State>() {
            @Override 
            public void changed(ObservableValue<? extends Worker.State> ov, Worker.State oldState, Worker.State newState) {
                if(newState == Worker.State.SUCCEEDED) {
                    bind(sservice);
                    SaveCollectionService.setCloseApp(false);
                    sservice.restart();
                    
                    //send movie informations to server
                    gservice.restart(); 
                }//if
            }//changed(ObservableValue<? extends Worker.State>, Worker.State, Worker.State)
       });//addListener(ChangeListener<Worker.State>)
        
         // internet failure dialog box and ok-confirm-button
        internetFailureOkButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                hideModalMessage();
                internetFailure.setVisible(false);
            }//handle(ActionEvent)
        });//setOnAction(EventHandler<ActionEvent>)
        
        // customize delete movies button
        deleteMoviesButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(!treeView.getSelectionModel().isEmpty() 
                && allRootNode != null  && allRootNode.getValue().getIndex() != -3) {
                    confirmDelete.setVisible(true);
                    showModalMessage(confirmDelete);
                }//if
            }//handle(ActionEvent)            
        });//setOnAction(EventHandler<ActionEvent>)
        
        // customize confirm delete process
        confirmDeleteNoButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                hideModalMessage();
                confirmDelete.setVisible(false);
            }//handle(ActionEvent)
        });//setOnAction(ActionEvent<ActionEvent>)
        confirmDeleteYesButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                bind(dservice);
                dservice.restart();
            }//handle(ActionEvent)
        });//setOnAction(EventHandler<ActionEvent>)
        
        // auto save when DeleteMovieService is over
        dservice.stateProperty().addListener(new ChangeListener<Worker.State>() {
            @Override public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                if(newState == Worker.State.SUCCEEDED) {
                    bind(sservice);
                    SaveCollectionService.setCloseApp(false);
                    sservice.restart();
                }//if
            }//changed(ObservableValue<? extends Worker.State>, Worker.State, Worker.State)
       });//addListener(ChangeListener<Worker.State>)
                
        // customize save button behavior
        saveCollectionButton.setOnAction(new EventHandler<ActionEvent>() {
           @Override
           public void handle(ActionEvent t) {
               if(allRootNode != null 
               && allRootNode.getValue().getIndex() != -3) {
                   bind(sservice);
                   SaveCollectionService.setCloseApp(false);
                   sservice.restart();
               }//if
           }//handle(ActionEvent)
        });//setOnAction(EventHandler<ActionEvent>)
        
        // customize alreadyWatchedButton behavior
        alreadyWatchedButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(!treeView.getSelectionModel().isEmpty() 
                && allRootNode != null && allRootNode.getValue().getIndex() != -3) {
                    bind(aservice);
                    aservice.restart();
                }//if
            }//handle(ActionEvent)
        });//setOnAction(EventHandler<ActionEvent>)
        
        // save collection changes after AlreadyWatchedService is over
        aservice.stateProperty().addListener(new ChangeListener<Worker.State>() {
            @Override public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                if(newState == Worker.State.SUCCEEDED) {
                    bind(sservice);
                    SaveCollectionService.setCloseApp(true);
                    sservice.restart();
                }//if
            }//changed(ObservableValue<? extends Worker.State>, Worker.State, Worker.State)
       });//addListener(ChangeListener<Worker.State>)
        
        // customize behavior of playMovieButton
        playMovieButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(!treeView.getSelectionModel().isEmpty()
                && treeView.getSelectionModel().getSelectedItem() != treeView.getRoot()
                && allRootNode != null && allRootNode.getValue().getIndex() != -3) {
                   bind(pservice);
                   pservice.restart();
                }//if
            }//handle(ActionEvent)
        });//setOnAction(EventHandler<ActionEvent>)
        
        // customize the behavior of checkmarkButton
        checkmarkButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(!treeView.getSelectionModel().isEmpty() 
                && allRootNode != null
                && allRootNode.getValue().getIndex() != -3) {
                    bind(cservice);
                    cservice.restart();
                }//if
            }//handle(ActionEvent)
        });//setOnAction(EvenetHandler<ActionEvent>)
        
        // save collection changes after AlreadyWatchedService is over
        cservice.stateProperty().addListener(new ChangeListener<Worker.State>() {
            @Override public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                if(newState == Worker.State.SUCCEEDED) {
                    bind(sservice);
                    SaveCollectionService.setCloseApp(true);
                    sservice.restart();
                }//if
            }//changed(ObservableValue<? extends Worker.State>, Worker.State, Worker.State)
       });//addListener(ChangeListener<Worker.State>)
        
        // customize the behavior of refresh movies information
        refreshMoviesButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(!treeView.getSelectionModel().isEmpty() 
                && allRootNode != null && allRootNode.getValue().getIndex() != -3) {
                    bind(rservice);
                    rservice.restart();
                }//if
            }//handle(ActionEvent)            
        });//setOnAction(EventHandler<ActionEvent>)
        
        // save collection changes after AlreadyWatchedService is over
        rservice.stateProperty().addListener(new ChangeListener<Worker.State>() {
            @Override public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                if(newState == Worker.State.SUCCEEDED) {
                    bind(sservice);
                    SaveCollectionService.setCloseApp(false);
                    sservice.restart();
                }//if
            }//changed(ObservableValue<? extends Worker.State>, Worker.State, Worker.State)
       });//addListener(ChangeListener<Worker.State>)
        
        // customize the behavior of optionsMenu ok button
        optionsMenuOkButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(optionsMenuNameField.getText().length() > 0) {
                    MainWindowController.displayedMovies.get(((MovieTreeItem)treeView.getSelectionModel()
                                              .getSelectedItem())
                                              .getValue().getIndex())
                                              .setMovieName(optionsMenuNameField.getText());
                    
                    ((MovieTreeItem)treeView.getSelectionModel().getSelectedItem()).getItem().setText(optionsMenuNameField.getText());
                    
                    ((MovieTreeItem)treeView.getSelectionModel()
                                              .getSelectedItem())
                                              .setExpanded(true);
                    ((MovieTreeItem)treeView.getSelectionModel()
                                              .getSelectedItem())
                                              .setExpanded(false);
                }//if
            }//handle(ActionEvent)           
        });//setOnAction(EventHandle<ActionEvent>)
        
        // close options menu
        optionsMenuCloseButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                optionsMenu.setVisible(false);
            }//handle(ActionEvent)            
        });//setOnAction(EventHandler)
        
        // customize the behavior of optionsMenu refresh button
        optionsMenuRefreshButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(optionsMenuTitleField.getText().length() > 0) {
                   bind(tservice);
                   tservice.restart();
                }//if
            }//handle(ActionEvent)           
        });//setOnAction(EventHandler<ActionEvent>)
        
        // restrict  optionsMenuYearField to take only numbers
        optionsMenuYearField.lengthProperty().addListener(new ChangeListener<Number>(){
             @Override
             public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {              
                   if(newValue.intValue() > oldValue.intValue()){
                       char ch = optionsMenuYearField.getText().charAt(oldValue.intValue());
                       if(!(ch >= '0' && ch <= '9' )) {       
                            optionsMenuYearField.setText(optionsMenuYearField.getText()
                                                           .substring(0, optionsMenuYearField.getText()
                                                                                          .length()-1)); 
                       }//if
                  }//if
             }//changed(ObservableValue<? extends Number>, Number, Number)
        });//addListener(ChangeListener<Number>)
        
        // hide optionsMenu when treeView is not focused
        treeView.focusedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
                if(!t1
                && !optionsMenu.isFocused()
                && !optionsMenuYearField.isFocused()
                && !optionsMenuNameField.isFocused()
                && !optionsMenuTitleField.isFocused()
                && !optionsMenuOkButton.isFocused()
                && !optionsMenuRefreshButton.isFocused()) {
                    optionsMenu.setVisible(false);
                    optionsMenuYearField.setText("");
                    optionsMenuTitleField.setText("");
                    optionsMenuNameField.setText("");
                }//if
            }//changed(ObservableValue<? extends Boolean>, Boolean, Boolean)         
        });//addListener(ChangeListener<Boolean>)
        
        // remove ListChangeListener from old root and add ListChangeListener to the new one
        treeView.rootProperty().addListener(new ChangeListener<MovieTreeItem>() {
            @Override
            public void changed(ObservableValue<? extends MovieTreeItem> ov, MovieTreeItem oldValue, MovieTreeItem newValue) {
                if(newValue != null && newValue != oldValue) {
                    if(oldValue != null)
                        oldValue.getChildren().removeListener(childrenListener);
                    
                    newValue.getChildren().addListener(childrenListener);
                    newValue.setExpanded(true);
                }else if(newValue == null) {
                    pressedButtonInfo.setText("0 movies displayed...");
                }//else if
            }//changed()
        });//addListener(ChangeListener<MovieTreeItem>)
        
        //open requests/suggestions window
        messagesButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                requestsMessagesWindow.setVisible(true);
                modalDimmerEventHandler.setHideOnClick(true);
                showModalMessage(requestsMessagesWindow);
            }//handle(ActionEvent)
        });//setOnAction(EventHandler<ActionEvent>)
        
        // remove <new>  annotation from expanded messages, set this message opened
        requests.expandedPaneProperty().addListener(new ChangeListener<TitledPane>() {
            @Override
            public void changed(ObservableValue<? extends TitledPane> ov, TitledPane old_val, TitledPane new_val) {
                if (new_val != null) {
                    if(!((pkg4ax.mc.graphics.Request)requests.getExpandedPane()).getRequest().getOpened()) {
                        requests.getExpandedPane().setGraphic(new ImageView(message_read));
                        ((pkg4ax.mc.graphics.Request)requests.getExpandedPane()).getRequest().setOpened(true);
                        unreadMessages.set(unreadMessages.get() - 1);
                    }//if
                }//if
            }//changed(ObservableValue<? extends TitledPane>, TitledPane, TitledPane)
        });//addListener(ChangeListener<TitledPan   e>)
        
        // remove <new>  annotation from expanded messages, set this message opened
        suggestions.expandedPaneProperty().addListener(new ChangeListener<TitledPane>() {
            @Override
            public void changed(ObservableValue<? extends TitledPane> ov, TitledPane old_val, TitledPane new_val) {
                if (new_val != null) {
                    if(!((pkg4ax.mc.graphics.Suggestion)suggestions.getExpandedPane()).getSuggestion().getOpened()) {
                        suggestions.getExpandedPane().setGraphic(new ImageView(message_read));
                        ((pkg4ax.mc.graphics.Suggestion)suggestions.getExpandedPane()).getSuggestion().setOpened(true);
                        unreadMessages.set(unreadMessages.get() - 1);
                    }//if
                }//if
            }//changed(ObservableValue<? extends TitledPane>, TitledPane, TitledPane)
        });//addListener(ChangeListener<TitledPane>)
        
        // customize the behavior of logout button
        logoutButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                System.out.println("\nRunning Threads: "+runningThreads+"....\n");
                if(runningThreads == 0) {
                    ThreadClient.logout = true;
                    Thread thread = new ThreadClient(new LogoutService(userId));
                    thread.start();
                    try {
                        thread.join();
                    }catch(InterruptedException ie) {
                        ie.printStackTrace();
                    }//catch
                    //sservice.restart();
                    mservice.restart();
                }else {
                    internetFailure.setVisible(true);
                    ((Text)((HBox)internetFailure.getChildren()
                                                  .get(0))
                                                  .getChildren()
                                                  .get(0))
                            .setText("Application is still gathering informations in the background "
                                    + "for the movies you've just added."
                                    + "PLEASE WAIT untill the process is completed and then log out.");
                    showModalMessage(internetFailure);
                }//else
            }//handle            
        });//setOnAction(EventHandler<ActionEvent>)
        
        // exit after saving messages
        mservice.stateProperty().addListener(new ChangeListener<Worker.State>() {
            @Override public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) {
                if(newState == Worker.State.SUCCEEDED) {
                    while(!ThreadClient.finished)
                        try {
                            Thread.sleep(200);
                        }catch(InterruptedException ie) {}
                    System.exit(0);
                }//if
            }//changed(ObservableValue<? extends Worker.State>, Worker.State, Worker.State)
       });//addListener(ChangeListener<Worker.State>)
        
      
        //customize the behavior of send request button
        profileButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                new ThreadClient(new GetProfileInfoService(userId)).start();
                
                editInfoButton.setDisable(false);
                changePictureButton.setDisable(false);
                showModalMessage(myProfileSplitPane);
                myProfileSplitPane.setVisible(true);
            }//handle(ActionEvent)            
        });//setOnAction(EventHandler<ActionEvent>)
        
        changePictureButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                FileChooser fileChooser = new FileChooser();
               //Set extension filter
                FileChooser.ExtensionFilter extFilterJPG = new FileChooser.ExtensionFilter("JPG files (*.jpg)", "*.JPG");
                FileChooser.ExtensionFilter extFilterPNG = new FileChooser.ExtensionFilter("PNG files (*.png)", "*.PNG");
                fileChooser.getExtensionFilters().addAll(extFilterJPG, extFilterPNG);
                //Show open file dialog
                chosenProfilePhoto = fileChooser.showOpenDialog(null);
                BufferedImage bufferedImage = null;
                try {
                    bufferedImage = ImageIO.read(chosenProfilePhoto);
                }catch (IOException ex) {
                    ex.printStackTrace();
                }//catch
                Image image = SwingFXUtils.toFXImage(bufferedImage, null);
                imgView.setImage(image); 
            }//handle
        });//changePictureButton set on action        
        editInfoButton.setOnAction(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent t) {
                nickNameTextF.setEditable(true);
                nickNameTextF.setId("textfield2");
                countryTextF.setEditable(true);
                countryTextF.setId("textfield2");
                cityTextF.setEditable(true);
                cityTextF.setId("textfield2");
                saveChanges.setVisible(true);
            }//handle
        });//edit info button set on action
        
        saveChanges.setOnAction(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent t) {
                if(chosenProfilePhoto == null)
                    new ThreadClient(new SaveProfileChangesService(nickNameTextF.getText().trim(), false)).start();
                else
                    new ThreadClient(new SaveProfileChangesService(nickNameTextF.getText().trim(), true)).start();
                    
                nickNameTextF.setEditable(false);
                nickNameTextF.setId("textfield");
                countryTextF.setEditable(false);
                countryTextF.setId("textfield");
                cityTextF.setEditable(false);
                cityTextF.setId("textfield");
                saveChanges.setVisible(false);
            }//handle(ActionEvent)
        });//save changes setOnAction
        
        closeProfileButton.setOnAction(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent t) {
                myProfileSplitPane.setVisible(false);
                hideModalMessage();
            }//handle
        
        });//closeProfileButton on Action
        
                                            //****************MyProfile actions buttons************
        
       // add context menu to list
        final ContextMenu contextMenu = new ContextMenu();                  
        MenuItem sugg = new MenuItem("Suggest");
        MenuItem poke = new MenuItem("Poke");                       
        MenuItem goTo = new MenuItem("Go to Profile");
        goTo.setOnAction(new EventHandler<ActionEvent>(){   
            @Override
            public void handle(ActionEvent t) {
                if(!contactList.getSelectionModel().isEmpty() && contactList.getSelectionModel().getSelectedItem() != null) {
                    new ThreadClient(new GetProfileInfoService(contactList.getSelectionModel().getSelectedItem())).start();
                    //modalDimmerEventHandler.setHideOnClick(true);
                    editInfoButton.setDisable(true);
                    changePictureButton.setDisable(true);
                    showModalMessage(myProfileSplitPane);
                    myProfileSplitPane.setVisible(true);
                }//if
            }//handle
        });//goTo setOnAction
        
        sugg.setStyle(null);
        sugg.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                suggestToContact.fire();
            }//handle(ActionEvent)          
        });//setOnAction(EventHandler)
        contextMenu.getItems().addAll(sugg, poke, goTo);
        contactList.setContextMenu(contextMenu);
        contactList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
       // customize the behavior of suggestToContact button, 
       //for each selected movie show input prompt to give download link for file and subs
        suggestToContact.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if(!treeView.getSelectionModel().isEmpty()
                && !contactList.getSelectionModel().isEmpty()
                && treeView.getRoot() != null
                && allRootNode.getValue().getIndex() != -3) {
                    movieLinkTextField.setText("");
                    movieSubsTextField.setText("");
                    
                    movieSuggestionsToSend.clear();
                    iterator = (Iterator<MovieTreeItem>)treeView.getSelectionModel().getSelectedItems().iterator();
                    currentMovieTreeItem = iterator.next();
                    
                    nextTitle.set(displayedMovies.get(currentMovieTreeItem.getItem().getIndex()).getMovieName());
                    next.set(iterator.hasNext());
                    nextSelectedMovieButton.visibleProperty().bind(next);
                    movieTitleLabel.textProperty().bind(nextTitle);
                    
                    suggestMovieDialog.setVisible(true);
                    modalDimmerEventHandler.setHideOnClick(true);
                    showModalMessage(suggestMovieDialog);
                }//if
            }//handle(ActionEvent)
        });//setOnAction(EventHandler<ActionEvent>)
        nextSelectedMovieButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                Suggestion s = new pkg4ax.mc.utilities.Suggestion(userId, "4", (String)displayedMovies
                                                                                    .get(currentMovieTreeItem
                                                                                    .getValue()
                                                                                    .getIndex())
                                                                                    .getMovieAttributes()
                                                                                    .getMovie()
                                                                                    .get("imdbID"), 
                                                                         new Timestamp(Calendar.getInstance().getTimeInMillis()), 
                                                                         movieLinkTextField.getText(),
                                                                         movieSubsTextField.getText());
                
                s.setNickName(nickName);
                s.setMovieTitle(movieTitleLabel.getText());
                movieSuggestionsToSend.add(s);
                nextTitle.set(displayedMovies.get(iterator.next().getItem().getIndex()).getMovieName());
                next.set(iterator.hasNext());
                movieLinkTextField.setText("");
                movieSubsTextField.setText("");
            }//handle(ActionEvent)
        });//setOnAction(EventHandler)
        suggestionOkButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                hideModalMessage();
                suggestMovieDialog.setVisible(false);
                
                Suggestion s = new pkg4ax.mc.utilities.Suggestion(userId, "", (String)displayedMovies
                                                                                    .get(currentMovieTreeItem
                                                                                    .getValue()
                                                                                    .getIndex())
                                                                                    .getMovieAttributes()
                                                                                    .getMovie()
                                                                                    .get("imdbID"), 
                                                                     new Timestamp(Calendar.getInstance().getTimeInMillis()), 
                                                                     movieLinkTextField.getText(),
                                                                     movieSubsTextField.getText());
       
                s.setNickName(nickName);
                s.setMovieTitle(movieTitleLabel.getText());
                movieSuggestionsToSend.add(s);
                List<Integer> contactsIds = new ArrayList<>(2);
                for(Integer integer: contactList.getSelectionModel().getSelectedItems())
                    contactsIds.add(integer);
                new ThreadClient(new SendSuggestionService(movieSuggestionsToSend, contactsIds)).start();
            }//handle(ActionEvent)
        });//setOnAction(EventHandler)
        
        // customize list cell
        contactList.setCellFactory(new Callback<ListView<Integer>, ListCell<Integer>>() {
            @Override
            public ListCell<Integer> call(ListView<Integer> param) {
                return new ListCell<Integer>() {
                    final ImageView img = new ImageView();
                    @Override
                    public void updateItem(Integer id, boolean empty) {
                        super.updateItem(id, empty);
                        if(empty) {
                            setText(null);
                            setGraphic(null);
                        }else {
                           setText(displayedContacts.get(id).getNickName());
                           img.setImage((displayedContacts.get(id).online())? onlineStatus : offlineStatus);
                           setGraphic(img);
                        }//else
                    }//updateItem(Integer, boolean)
                };//ListCell<Integer>()
            }//call(ListView<Integer>)
        });//setCellFactory(CallBack<ListView<Integer>>, ListCell<Integer>)
        
        //customize the behavior of addContact button
        addContact.setOnAction(new EventHandler<ActionEvent>(){      //add on action
            @Override                                          
            public void handle(ActionEvent t) { 
                adder.setVisible(true);
                checker.setAlignment(Pos.CENTER);
                modalDimmer.setStyle("-fx-background-color: rgba(0, 0, 0, 0.1)");
                showModalMessage(adder);
                //showAdder();    
            }//handle(ActionEvent)
         });//setOnAction(EventHandler)
        
        // customize the behavior of delete contact button                  
        deleteContact.setOnAction(new EventHandler<ActionEvent>(){   // delete on action
           @Override
            public void handle(ActionEvent t) {
                System.out.println("delete button pressed");
                if(!contactList.getSelectionModel().isEmpty()) {
                    checker.setVisible(true);
                    checker.setAlignment(Pos.CENTER);
                    modalDimmer.setStyle("-fx-background-color: rgba(0, 0, 0, 0.1)");
                    showModalMessage(checker);
                }//if
            }//handle(ActionEvent)
        });//setOnAction(EventHandler)
        // confirm contact delete
        checkerButtonYes.setOnAction(new EventHandler <ActionEvent>() {           
            @Override
            public void handle(ActionEvent t) {
                removeFromList();
                hideModalMessage();
                modalDimmer.setStyle("-fx-background-color: rgba(0 , 0, 0, 0.5);");
            }//
        });//checkbuttonYes action
        // rollback
        checkerButtonNo.setOnAction(new EventHandler <ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                hideModalMessage();
                modalDimmer.setStyle("-fx-background-color: rgba(0 , 0, 0, 0.5);");
            }//handle
        });//setOnAction
        // add contact by email
        nameField.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent t) {
                if(t.getCode().equals(KeyCode.ENTER)){
                    Request request = new Request(userId, nickName, null , new Timestamp(Calendar.getInstance().getTimeInMillis()));
                    request.setEmail(nameField.getText().trim());
                    new ThreadClient(new SendRequestService(request)).start();
                    System.out.println(nameField.getText());
                    
                    nameField.setText("");
                    adder.setVisible(false);
                    hideModalMessage();
                    modalDimmer.setStyle("-fx-background-color: rgba(0 , 0, 0, 0.5);");
                    // sendFriendReq(text);
                }//if
                if(t.getCode().equals(KeyCode.ESCAPE)){
                    nameField.setText("");
                    adder.setVisible(false);
                    hideModalMessage();
                    modalDimmer.setStyle("-fx-background-color: rgba(0 , 0, 0, 0.5);");
                }//if
            }//handle(KeyEvent) 
        });//setOnKeyPressed(EventHandler<KeyEvent>)  
        
       treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<MovieTreeItem>() {
            @Override
            public void changed(ObservableValue<? extends MovieTreeItem> ov, MovieTreeItem oldValue, MovieTreeItem newValue) {
                if(newValue != null) {
                    if(newValue != treeView.getRoot() && displayedMovies.get(newValue.getItem().getIndex()).getMovieAttributes().getMovie() != null) {
                        new ThreadClient(new pkg4ax.mc.serverServices.GetMovieInformationsService((String)displayedMovies.get(newValue.getItem().getIndex()).getMovieAttributes().getMovie().get("imdbID"))).start();
                        //System.out.println(displayedMovies.get(newValue.getItem().getIndex()).getMovieAttributes().getMovie().toJSONString());
                        JSONObject json = displayedMovies.get(newValue.getItem().getIndex()).getMovieAttributes().getMovie();
                        movieName.setText((json.containsKey("Title"))? (String)json.get("Title") : displayedMovies.get(newValue.getItem().getIndex()).getMovieName());
                        movieYear.setText((json.containsKey("Year"))? (String)json.get("Year") : "N/A");
                        language.setText((json.containsKey("Language"))? (String)json.get("Language") : "N/A");
                        rated.setText((json.containsKey("Rated"))? (String)json.get("Rated") : "N/A");
                        duration.setText((json.containsKey("Runtime"))? (String)json.get("Runtime") : "N/A");
                        releaseDate.setText((json.containsKey("Released"))? (String)json.get("Released") : "N/A");
                        genres.setText((json.containsKey("Genre"))? (String)json.get("Genre") : "N/A");
                        ratingNumber.setText((json.containsKey("imdbRating"))? (String)json.get("imdbRating") : "N/A");
                        votes.setText((json.containsKey("imdbVotes"))? (String)json.get("imdbVotes") : "N/A");
                        movieType.setText((json.containsKey("Type"))? (String)json.get("Type") : "N/A");
                        metascore.setText((json.containsKey("Metascore"))? (String)json.get("Metascore") : "N/A");
                        starList.setText((json.containsKey("Actors"))? (String)json.get("Actors") : "N/A");
                        writer.setText((json.containsKey("Writer"))? (String)json.get("Writer") : "N/A");
                        director.setText((json.containsKey("Director"))? (String)json.get("Director") : "N/A");
                        plotArea.setText((json.containsKey("Plot"))? (String)json.get("Plot") : "N/A");
                        countryMovie.setText((json.containsKey("Country"))? (String)json.get("Country") : "N/A");
                        fileDuration.setText(displayedMovies.get(newValue.getItem().getIndex()).getFFMpegResults().getFileDuration());
                        bitrate.setText(displayedMovies.get(newValue.getItem().getIndex()).getFFMpegResults().getBitRate());
                        videoFormat.setText(displayedMovies.get(newValue.getItem().getIndex()).getFFMpegResults().getVideoFormat());               
                        audioFormat.setText(displayedMovies.get(newValue.getItem().getIndex()).getFFMpegResults().getAudioFormat());
                        creationTime.setText(displayedMovies.get(newValue.getItem().getIndex()).getFFMpegResults().getCreationTime());
                        if(json.containsKey("Poster") && !((String)json.get("Poster")).equalsIgnoreCase("N/A")) {
                            System.err.println("LOADING POSTER\n....");
                            FileInputStream fis = null;
                            try {
                                fis = new FileInputStream(new File("src/pkg4ax/mc/posters/"+displayedMovies.get(newValue.getItem().getIndex()).getMovieAttributes().getMovie().get("imdbID")));
                                poster.setImage(new Image(fis));
                            }catch (FileNotFoundException fnfe) {
                                fnfe.printStackTrace();
                            }finally {
                                if(fis != null)
                                    try {
                                        fis.close();
                                    }catch(IOException ioe) {
                                        ioe.printStackTrace();
                                    }//catch
                            }//finally
                        }else  {
                            System.out.println("NO POSTER...");
                            poster.setImage(initImage);
                        }//else
                    }else {
                        movieName.setText(displayedMovies.get(newValue.getItem().getIndex()).getMovieName());
                        movieYear.setText("N/A");
                        language.setText("N/A");
                        rated.setText("N/A");
                        duration.setText("N/A");
                        releaseDate.setText("N/A");
                        genres.setText("N/A");
                        ratingNumber.setText("N/A");
                        votes.setText("N/A");
                        movieType.setText("N/A");
                        metascore.setText("N/A");
                        starList.setText("N/A");
                        writer.setText("N/A");
                        director.setText("N/A");
                        plotArea.setText("N/A");
                        countryMovie.setText("N/A");
                        poster.setImage(initImage);
                    }//else         
                }//if
            }//changed(ObservableValue<? extends MovieTreeitem>, MovieTreeItem, MovieTreeItem)
        });//addListener(ChangeListener<MovieTreeItem>())
        
        adder.setVisible(false);
        adder.getStyleClass().add("adderStyle");                //adder
        addLabel.getStyleClass().add("addLabel");
        nameField.setPromptText("Friends Name");
        nameField.getStyleClass().add("nameField");
        
        checker.setVisible(false);
                                                                    //checker
        checker.getStyleClass().add("checkerStyle");
        topCheckerLabel.getStyleClass().add("topCheckerLabel");
        checkerVB.getStyleClass().add("vboxCheck");
        checkerButtonYes.getStyleClass().add("checkerButtonYes");
        checkerButtonNo.getStyleClass().add("checkerButtonNo");
       
        //================================================INITIALIZE FOR SEARCH BAR==============================//

        
        warningLabel.setVisible(false);
        advancedPane.setVisible(false);
        advancedAccordion.setExpandedPane(null);
        windowBtns = new WindowButtons(MC.applicationStage);
        windowBtns.setAlignment(Pos.TOP_RIGHT);
        windowVBox.getChildren().add(windowBtns);

        mainToolBar.getChildren().add(windowBtns);
        genreList = new GenreList();
        awardList = new AwardList();

        genreList.prefWidthProperty().bind(genreVBox.prefWidthProperty());
        genreList.prefHeightProperty().bind(genreVBox.prefHeightProperty());
        awardList.prefWidthProperty().bind(awardVBox.prefWidthProperty());
        awardList.prefHeightProperty().bind(awardVBox.prefHeightProperty());

        awardVBox.getChildren().add(awardList);
        genreVBox.getChildren().add(genreList);
        
        fromRating.textProperty().addListener(new NumberListener(fromRating, 3));
        fromRating.textProperty().addListener(new NumberListener(fromRating, 3));
        fromYear.textProperty().addListener(new NumberListener(fromYear, 1));
        toYear.textProperty().addListener(new NumberListener(toYear, 1));
        fromVotes.textProperty().addListener(new NumberListener(fromVotes, 2));
        toVotes.textProperty().addListener(new NumberListener(toVotes, 2));
        fromLikes.textProperty().addListener(new NumberListener(fromLikes, 2));
        toLikes.textProperty().addListener(new NumberListener(toLikes, 2));
        fromSuggestions.textProperty().addListener(new NumberListener(fromSuggestions, 2));

        toSuggestions.textProperty().addListener(new NumberListener(toSuggestions, 2));

        searchGroup.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {

            @Override
            public void handle(KeyEvent t) {
                if (t.getCode() == KeyCode.ENTER) {
                    searchBtn.fire();
                }
            }
        });
        advancedBtn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent t) {
                if (!advancedPane.isVisible()) {
                    new ThreadClient(new GetGenresService()).start();
                    new ThreadClient(new GetAwardsService()).start();
                    modalDimmerEventHandler.setHideOnClick(true);
                    modalDimmer.setStyle("-fx-background-color:rgba(0,0,0,0.6)");
                    showModalMessage(advancedPane);
                    advancedPane.setVisible(true);
                    
                } else {
                    advancedPane.setVisible(false);
                    advancedAccordion.setExpandedPane(null);
                    advancedBtn.setSelected(false);
                }
            }
        });
        searchBtn.setOnAction(new SearchHandler());

    }//initialize(URL, ResourceBundle)
    
    // change contact status
     public static void changeContactStatus(final Contact c) {
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                ObservableList<Integer> list = MainWindowController.getController().contactList.getItems();
                MainWindowController.getController().contactList.setItems(null);
                displayedContacts.get(c.getId()).setStatus(c.online());
                MainWindowController.getController().contactList.setItems(list);
            }//run()
        });//runLater(Runnable)
    }//changeContactStatus(Contact)
    
    // load most recent collection at startUp
    @SuppressWarnings("unchecked")
    private void loadRecentCollection() {
        BufferedReader br = null;
        String recentCollection;
        
        try {
            br = new BufferedReader(new FileReader(new File("src/pkg4ax/mc/config/rc.dat")));
            recentCollection = br.readLine();
           
            if(recentCollection == null) {
                displayedMovies.put(-3, new Movie("Add movie(s) here using \"add\" button above."));
                allRootNode = new MovieTreeItem(new IndexMovieTreeItem(-3, 0, ""));
                // set the tree view root node, by default is the collection's name, expanded
                treeView.setRoot(allRootNode);
                // initialize label pressedButtonInfo text
                pressedButtonInfo.setText(treeView.getRoot().getChildren().size()+" movies displayed...");
            }else{
                bind(oservice);
                OpenCollectionService.setCollectionName(recentCollection.trim());
                oservice.start();
            }//else
        }catch(FileNotFoundException fnfe) {
            fnfe.printStackTrace();
        }catch(IOException ioe) {
            ioe.printStackTrace();
        }finally {
            if(br != null)
                try {
                    br.close();
                }catch(IOException ioe) {
                    ioe.printStackTrace();
                }//catch
        }//finally
    }//loadRecentCollection()
    
    // get the MainWindowController object after been auto-initialized
    public static MainWindowController getController() {
        return mainWindowController;
    }//getController()
    
    // hide current promt window with effect
    public void hideModalMessage() {
        modalDimmer.setCache(true);
        TimelineBuilder.create().keyFrames(
            new KeyFrame(Duration.seconds(1), 
                new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent t) {
                        modalDimmer.setCache(false);
                        modalDimmer.setVisible(false);
                        modalDimmer.getChildren().clear();
                    }//handle(ActionEvent)
                },
                new KeyValue(modalDimmer.opacityProperty(),0, Interpolator.EASE_BOTH)
        )).build().play();
    }//hideModalMessage()
    
    // show window on demmand or not with effect
    public void showModalMessage(Node message) {
        if(!modalDimmer.getChildren().contains(message))
            modalDimmer.getChildren().add(message);
        
        modalDimmer.setOpacity(0);
        modalDimmer.setVisible(true);
        modalDimmer.setCache(true);
        TimelineBuilder.create().keyFrames(
            new KeyFrame(Duration.seconds(1), 
                new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent t) {
                        modalDimmer.setCache(false);
                    }//handle(ActionEvent)
                },
                new KeyValue(modalDimmer.opacityProperty(),1, Interpolator.EASE_BOTH)
        )).build().play();
    }//showModalMessage(Node)
    
    // create index for collection to specified index location for the given data
    public void createIndex(String filename, String indexLocation) {
        indexer = new Indexer(filename, indexLocation);  
        try {
            indexer.create();
        }catch(Exception e) {
              e.printStackTrace();
        }//catch
    }//createIndex(String, String, Data)
    
    // bind a service with stackRegion and progressIndicator
    public void bind(Service s) {
        // unbind
        stackProgressIndicator.progressProperty().unbind();
        stackRegion.visibleProperty().unbind();
        stackProgressIndicator.visibleProperty().unbind();
        progressIndicatorLabel.visibleProperty().unbind();
        progressIndicatorLabel.textProperty().unbind();
        // bind
        stackProgressIndicator.progressProperty().bind(s.progressProperty());
        stackRegion.visibleProperty().bind(s.runningProperty());
        stackProgressIndicator.visibleProperty().bind(s.runningProperty());
        progressIndicatorLabel.visibleProperty().bind(s.runningProperty());
        progressIndicatorLabel.textProperty().bind(s.messageProperty());
    }//bind(Service)
       
    public void removeFromList(){
        List<Integer> contactsToDelete = new ArrayList<>(2);
        for(Integer contact: contactList.getSelectionModel().getSelectedItems()) {
            displayedContacts.remove(contact);
            contactsToDelete.add(contact);
        }//for

        new ThreadClient(new DeleteContactService(contactsToDelete)).start();

        contactsToDelete = FXCollections.observableArrayList(contactList.getSelectionModel().getSelectedItems());
        contactList.getItems().removeAll(contactsToDelete);
        contactList.getSelectionModel().clearSelection();
    }//removeFromList()
    
    public synchronized static  void signalThread(int thread) {
        runningThreads = runningThreads + thread;
    }//threadFinished(int)
    
    public void changeProfileImage(InputStream is) {
        imgView.setImage(new Image(is));
    }//changeProfileImage(InputStream)
    
    public void addPosters(List<String> top5Posters) {
        ((ImageView)HboxTop1.getChildren().get(0)).setImage(null);
        ((ImageView)HboxTop2.getChildren().get(0)).setImage(null);
        ((ImageView)HboxTop3.getChildren().get(0)).setImage(null);
        ((ImageView)HboxTop4.getChildren().get(0)).setImage(null);
        ((ImageView)HboxTop5.getChildren().get(0)).setImage(null);
        if(top5Posters != null)
            for(int i=0; i<top5Posters.size(); i++) {
                URL url = null;
                URLConnection uc = null;
                try {
                    url = new URL(top5Posters.get(i));
                    uc = url.openConnection();
                    uc.addRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
                    uc.connect();
                    switch(i) {
                        case 0: ((ImageView)HboxTop1.getChildren().get(0)).setImage(new Image(uc.getInputStream())); break;
                        case 1: ((ImageView)HboxTop2.getChildren().get(0)).setImage(new Image(uc.getInputStream())); break;
                        case 2: ((ImageView)HboxTop3.getChildren().get(0)).setImage(new Image(uc.getInputStream())); break;
                        case 3: ((ImageView)HboxTop4.getChildren().get(0)).setImage(new Image(uc.getInputStream())); break;
                        case 4: ((ImageView)HboxTop5.getChildren().get(0)).setImage(new Image(uc.getInputStream()));
                    }//switch(int)
                }catch (MalformedURLException mue) {
                    mue.printStackTrace();
                }catch(IOException ioe) {
                    ioe.printStackTrace();
                }finally {
                    if(uc != null)
                        try {
                            if(uc.getInputStream() != null)
                                uc.getInputStream().close();
                        }catch(IOException ioe) {
                            ioe.printStackTrace();
                        }//catch
                }//finally
            }//for        
    }//addPosters(List<String>)
    
}//Class MainWindowController

